from abc import ABC, abstractmethod
from typing import Any, Dict, Optional, Type

from sqlalchemy import create_engine
from sqlalchemy.engine import Engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session

from app.core.config import settings

# 创建SQLAlchemy基类
Base = declarative_base()

class DBFactory(ABC):
    """数据库工厂抽象基类
    
    定义数据库连接工厂的接口，所有具体数据库工厂类都应继承此类
    """
    @abstractmethod
    def create_engine(self) -> Engine:
        """创建数据库引擎
        
        Returns:
            Engine: SQLAlchemy引擎实例
        """
        pass
    
    @abstractmethod
    def create_session_local(self) -> sessionmaker:
        """创建会话工厂
        
        Returns:
            sessionmaker: SQLAlchemy会话工厂
        """
        pass
    
    @abstractmethod
    def get_db(self) -> Session:
        """获取数据库会话
        
        用于依赖注入
        
        Yields:
            Session: 数据库会话
        """
        pass

class PostgreSQLFactory(DBFactory):
    """PostgreSQL数据库工厂
    
    实现PostgreSQL数据库连接的创建
    """
    def __init__(self, connection_uri: Optional[str] = None):
        """初始化PostgreSQL工厂
        
        Args:
            connection_uri: 数据库连接URI，如不提供则使用配置中的URI
        """
        self.connection_uri = connection_uri or str(settings.SQLALCHEMY_DATABASE_URI)
        self._engine = None
        self._session_local = None
    
    def create_engine(self) -> Engine:
        """创建PostgreSQL引擎
        
        Returns:
            Engine: SQLAlchemy引擎实例
        """
        if not self._engine:
            self._engine = create_engine(
                self.connection_uri,
                pool_pre_ping=True
            )
        return self._engine
    
    def create_session_local(self) -> sessionmaker:
        """创建PostgreSQL会话工厂
        
        Returns:
            sessionmaker: SQLAlchemy会话工厂
        """
        if not self._session_local:
            engine = self.create_engine()
            self._session_local = sessionmaker(autocommit=False, autoflush=False, bind=engine)
        return self._session_local
    
    def get_db(self) -> Session:
        """获取PostgreSQL数据库会话
        
        用于依赖注入
        
        Yields:
            Session: 数据库会话
        """
        session_local = self.create_session_local()
        db = session_local()
        try:
            yield db
        finally:
            db.close()

class MySQLFactory(DBFactory):
    """MySQL数据库工厂
    
    实现MySQL数据库连接的创建
    """
    def __init__(self, connection_uri: Optional[str] = None):
        """初始化MySQL工厂
        
        Args:
            connection_uri: MySQL数据库连接URI，如不提供则使用配置中的URI
        """
        self.connection_uri = connection_uri or settings.MYSQL_DATABASE_URI
        self._engine = None
        self._session_local = None
    
    def create_engine(self) -> Engine:
        """创建MySQL引擎
        
        Returns:
            Engine: SQLAlchemy引擎实例
        """
        if not self._engine:
            self._engine = create_engine(
                self.connection_uri,
                pool_pre_ping=True
            )
        return self._engine
    
    def create_session_local(self) -> sessionmaker:
        """创建MySQL会话工厂
        
        Returns:
            sessionmaker: SQLAlchemy会话工厂
        """
        if not self._session_local:
            engine = self.create_engine()
            self._session_local = sessionmaker(autocommit=False, autoflush=False, bind=engine)
        return self._session_local
    
    def get_db(self) -> Session:
        """获取MySQL数据库会话
        
        用于依赖注入
        
        Yields:
            Session: 数据库会话
        """
        session_local = self.create_session_local()
        db = session_local()
        try:
            yield db
        finally:
            db.close()

class SQLiteFactory(DBFactory):
    """SQLite数据库工厂
    
    实现SQLite数据库连接的创建
    """
    def __init__(self, db_path: str = "sqlite:///./app.db"):
        """初始化SQLite工厂
        
        Args:
            db_path: SQLite数据库文件路径
        """
        self.connection_uri = db_path
        self._engine = None
        self._session_local = None
    
    def create_engine(self) -> Engine:
        """创建SQLite引擎
        
        Returns:
            Engine: SQLAlchemy引擎实例
        """
        if not self._engine:
            self._engine = create_engine(
                self.connection_uri,
                connect_args={"check_same_thread": False},
                pool_pre_ping=True
            )
        return self._engine
    
    def create_session_local(self) -> sessionmaker:
        """创建SQLite会话工厂
        
        Returns:
            sessionmaker: SQLAlchemy会话工厂
        """
        if not self._session_local:
            engine = self.create_engine()
            self._session_local = sessionmaker(autocommit=False, autoflush=False, bind=engine)
        return self._session_local
    
    def get_db(self) -> Session:
        """获取SQLite数据库会话
        
        用于依赖注入
        
        Yields:
            Session: 数据库会话
        """
        session_local = self.create_session_local()
        db = session_local()
        try:
            yield db
        finally:
            db.close()

class DBFactoryManager:
    """数据库工厂管理器
    
    管理不同类型的数据库工厂，提供统一的接口获取数据库工厂
    """
    _factories: Dict[str, Type[DBFactory]] = {
        "postgresql": PostgreSQLFactory,
        "mysql": MySQLFactory,
        "sqlite": SQLiteFactory
    }
    
    @classmethod
    def register_factory(cls, db_type: str, factory_class: Type[DBFactory]) -> None:
        """注册数据库工厂
        
        Args:
            db_type: 数据库类型名称
            factory_class: 数据库工厂类
        """
        cls._factories[db_type] = factory_class
    
    @classmethod
    def get_factory(cls, db_type: str, **kwargs: Any) -> DBFactory:
        """获取数据库工厂
        
        Args:
            db_type: 数据库类型名称
            **kwargs: 传递给工厂构造函数的参数
            
        Returns:
            DBFactory: 数据库工厂实例
            
        Raises:
            ValueError: 当指定的数据库类型不存在时抛出
        """
        if db_type not in cls._factories:
            raise ValueError(f"不支持的数据库类型: {db_type}")
        
        # 为MySQL工厂添加连接URI参数
        if db_type == "mysql" and "connection_uri" not in kwargs:
            from app.core.config import settings
            kwargs["connection_uri"] = settings.MYSQL_DATABASE_URI
            
        return cls._factories[db_type](**kwargs)

# 默认使用PostgreSQL工厂
default_factory = DBFactoryManager.get_factory("postgresql")

# 导出默认数据库会话获取函数，用于依赖注入
get_db = default_factory.get_db